package at.cookieforensics;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

import java.util.ArrayList;

import javax.swing.JTree;
import javax.swing.JScrollPane;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JCheckBoxMenuItem;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.gargoylesoftware.htmlunit.util.Cookie;



/**
 * entry point of program
 * starts the UI
 * 
 * @author Martin Grottenthaler
 *
 */
public class UI extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * only UI JPanel
	 */
	private JPanel contentPane;
	/**
	 * top of tree
	 */
	private DefaultMutableTreeNode osTop;
	/**f
	 * OS info
	 */
	private String osStrings[];
	/**
	 * nodes in tree, one for every browser
	 * they are set if a browser is detected
	 */
	private DefaultMutableTreeNode chromeNode = null, firefoxNode = null, ieNode = null;
	/**
	 * if all cookies are shown or just cookies that are defined in services
	 * should be the same default value as checkbox
	 */
	private boolean showAll = false;
	/**
	 * services with cookies that can be restored
	 */
	private static ArrayList<String> services = new ArrayList<String>();
	/**
	 * tree UI element
	 */
	private JTree tree;
	/**
	 * all cookies in AllCookies class format
	 */
	private AllCookies allCookies = new AllCookies();
	/**
	 * JFrame icon
	 */
	private ImageIcon icon = new ImageIcon("icon.png");
	/**
	 * Launch the application.
	 * get supported services from xml file
	 */
	public static void main(String[] args) {
		// read xml for service list
		try {
			File xmlFile = new File("loginCodes.xml");
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(xmlFile);
			
			doc.getDocumentElement().normalize();
			
			NodeList nList = doc.getElementsByTagName("service");
			
			for (int i = 0; i < nList.getLength(); i++) {
				Node nNode = nList.item(i);
				
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					 
					Element eElement = (Element) nNode;
		 
					services.add(eElement.getAttribute("domain"));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// start ui
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					UI frame = new UI();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public UI() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(0, 0, 500, 500);
		setTitle("CookieForensics");
		
		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);
		
		JMenu fileMenu = new JMenu("File");
		menuBar.add(fileMenu);
		
		// refreshes cookies list
		JMenuItem refreshItem = new JMenuItem("Refresh");
		refreshItem.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				growNewTree();
			}
		});
		fileMenu.add(refreshItem);
		
		// changes whether all cookies or just the special cookies are shown
		// refreshes after this
		final JCheckBoxMenuItem allCheck = new JCheckBoxMenuItem("Show All Cookies");
		allCheck.setSelected(true);
		allCheck.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				showAll = allCheck.isSelected();
				growNewTree();
			}
		});
		fileMenu.add(allCheck);
		
		// exit application
		JMenuItem exitItem = new JMenuItem("Exit");
		exitItem.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				System.exit(NORMAL);
			}
		});
		fileMenu.add(exitItem);
		
		JMenu helpMenu = new JMenu("Help");
		menuBar.add(helpMenu);
		
		// about window
		JMenuItem aboutItem = new JMenuItem("About");
		aboutItem.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent arg0) {
				// TODO implement more sophisticated about dialog
				JOptionPane.showMessageDialog(null, "CookieForensics");
			}
		});
		
		JMenuItem supportedItem = new JMenuItem("Supported Services");
		supportedItem.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				JOptionPane.showMessageDialog(null, services.toString());
			}
		});
		
		helpMenu.add(supportedItem);
		helpMenu.add(aboutItem);
		
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		// get os
		osStrings = FindOS.findOS();
		String osString = osStrings[0] + ", " + osStrings[1] + ", " + osStrings[2];
		
		osTop = new DefaultMutableTreeNode(osString);
		
		// get cookies
		this.growTree();
		
		// add scroll bar
		JScrollPane scrollPane = new JScrollPane(tree);
		contentPane.add(scrollPane, BorderLayout.CENTER);
		this.setIconImage(icon.getImage());
	}
	
	/**
	 * removes tree from view so a new one can be created
	 */
	private void growNewTree() {
		osTop.removeAllChildren();
		
		contentPane.removeAll();
		contentPane.revalidate();
		contentPane.repaint();
		
		growTree();
	
		JScrollPane scrollPane = new JScrollPane(tree);
		contentPane.add(scrollPane, BorderLayout.CENTER);
	}
	
	/**
	 * creates a new tree
	 */
	private void growTree() {
		FindBrowser browsers = new FindBrowser(osStrings);
		
		if (browsers.foundChrome()) {
			chromeNode = new DefaultMutableTreeNode("Chrome " + browsers.getChromeVersion());
			osTop.add(chromeNode);
			
			Chrome chrome = new Chrome(osStrings[0]);
			ArrayList<Cookie> chromeCookies = chrome.getCookies();
			allCookies.setChromeCookies(chromeCookies);
			
			showCookies(chromeCookies, chromeNode);
		}
		
		if (browsers.foundFirefox()) {
			firefoxNode = new DefaultMutableTreeNode("Firefox " + browsers.getFirefoxVersion());
			osTop.add(firefoxNode);
			
			Firefox firefox = new Firefox(osStrings[0]);
			ArrayList<Cookie> firefoxCookies = firefox.getCookies();
			allCookies.setFirefoxCookies(firefoxCookies);
			
			showCookies(firefoxCookies, firefoxNode);
		}
		
		if (browsers.foundInternetExplorer()) {
			// TODO get IE version
			ieNode = new DefaultMutableTreeNode("Internet Explorer "/* + browsers.getIEVersion()*/);
			osTop.add(ieNode);
			
			InternetExplorer ie = new InternetExplorer(osStrings[0]);
			ArrayList<Cookie> ieCookies = ie.AllCookies;
			allCookies.setIeCookies(ieCookies);
			
			showCookies(ieCookies, ieNode);
		}
		
		tree = new JTree(osTop);
		
		MouseListener ml = new MouseListener() {
			public void mousePressed(MouseEvent e) {
				int selRow = tree.getRowForLocation(e.getX(), e.getY());
				TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
				
				if (selRow > 0) {
					if (SwingUtilities.isRightMouseButton(e)) {
						DefaultMutableTreeNode selectedElement = (DefaultMutableTreeNode) selPath.getLastPathComponent();
						
						// if leaf is selected make it look like parent is selected
						if (selectedElement.isLeaf()) {
							selectedElement = (DefaultMutableTreeNode) selectedElement.getParent();
						}
						
						// don't open popup if browser node is selected
						if (selectedElement.isNodeAncestor(chromeNode) && !selectedElement.equals(chromeNode) || selectedElement.isNodeAncestor(firefoxNode) && !selectedElement.equals(firefoxNode) || selectedElement.isNodeAncestor(ieNode) && !selectedElement.equals(ieNode)) {
							JPopupMenu popupMenu = new JPopupMenu();
							String domain = selectedElement.toString();
							ArrayList<Cookie> cookieList = new ArrayList<Cookie>();
							int browser = 0;
							
							JMenuItem item = new JMenuItem("request " + domain + " with cookies");
							
							if (selectedElement.getParent().toString().contains("Chrome")) {
								cookieList = allCookies.getCookiesFromDomain(1, domain);
								browser = 0;
							}
							
							if (selectedElement.getParent().toString().contains("Firefox")) {
								cookieList = allCookies.getCookiesFromDomain(2, domain);
								browser = 1;
							}
							
							if (selectedElement.getParent().toString().contains("Internet")) {
								cookieList = allCookies.getCookiesFromDomain(3, domain);
								browser = 2;
							}
							
							// TODO thats a very strange solution
							final ArrayList<Cookie> finalCookies = cookieList;
							final int finalBrowser = browser;
							
							item.addActionListener(new ActionListener() {
								public void actionPerformed(ActionEvent e) {
									// TODO html obviously not final version
									HtmlUnit htmlUnit = new HtmlUnit(finalBrowser);
									
									String cookieCheck = htmlUnit.checkCookie(finalCookies);
									JOptionPane.showMessageDialog(null, cookieCheck);
								}
							});
							popupMenu.add(item);
							popupMenu.show(e.getComponent(), e.getX(), e.getY());
						}
					}
				}
			}

			public void mouseClicked(MouseEvent arg0) {
				// nothing
				
			}

			public void mouseEntered(MouseEvent arg0) {
				// nothing
				
			}

			public void mouseExited(MouseEvent arg0) {
				// nothing
				
			}

			public void mouseReleased(MouseEvent arg0) {
				// nothing
				
			}
		};
		tree.addMouseListener(ml);
		
		contentPane.add(tree, BorderLayout.CENTER);
	}
	
	/**
	 * Shows cookies in UI
	 * Checks whether all Cookies or just defined Cookies should be shown
	 * 
	 * @param browserCookies ArrayList of browser cookies
	 * @param browserNode TreeNode to fill with cookie information
	 */
	private void showCookies(ArrayList<Cookie> browserCookies, DefaultMutableTreeNode browserNode) {
		ArrayList<String> domains = new ArrayList<String>();
		ArrayList<DefaultMutableTreeNode> cookieNodes = new ArrayList<DefaultMutableTreeNode>();
		
		for (Cookie cookie : browserCookies) {
			boolean contains = false;
			
			// remove '.' prefix
			if (cookie.getDomain().length()!=0 && cookie.getDomain().charAt(0) == '.') {
				String newDomain = cookie.getDomain().substring(1);
				
				cookie = new Cookie(newDomain, cookie.getName(), cookie.getValue(), cookie.getPath(), cookie.getExpires(), cookie.isSecure(), cookie.isHttpOnly());
			}
			
			if (!showAll) {
				contains = false;
				for (String service : services) {
					if (cookie.getDomain().contains(service)) {
						contains = true;
					}
				}
			}
			
			if (showAll || contains) {
				if (!(domains.contains(cookie.getDomain()))) {
					domains.add(cookie.getDomain());
					cookieNodes.add(new DefaultMutableTreeNode(cookie.getDomain()));
					browserNode.add(cookieNodes.get(cookieNodes.size()-1));
				}
				
				cookieNodes.get(cookieNodes.size()-1).add(new DefaultMutableTreeNode(cookie.toString()));
			}
		}
	}

	/**
	 * static method which returns supported services
	 * @return list of supported services
	 */
	public static ArrayList<String> getServices() {
		return services;
	}
}
